package mongo;

import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.ChangeStreamIterable;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.changestream.FullDocument;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;

import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry;
import static com.mongodb.client.model.Filters.eq;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

/**
 * @author : zGame
 * @version V1.0
 * @Project: mongo-java-demo
 * @Package mongo
 * @Description: TODO
 * @date Date : 2022年01月27日 14:39
 */
public class DbUtil {
    
    protected MongoClient mongoClient;
    
    protected MongoDatabase mongoAccess;
    
    protected ReplaceOptions replaceOptions;
    
    protected UpdateOptions updateOptions;
    //String uri = "mongodb://user:pass@sample.host:27017/?maxPoolSize=20&w=majority";
    protected String uri = "mongodb://gameplayer:gameplayer@192.168.2.66:27099/gameplayer";
    
    public DbUtil(String dbName) {
        init(dbName);
    }
    
    /**
     * watch 需要副本集
     *
     * @param dbName
     * @param watchCollection
     * @param callback
     */
    public DbUtil(String dbName, String watchCollection, Consumer callback) {
        init(dbName);
        MongoDatabase database = mongoClient.getDatabase(dbName);
        MongoCollection<Document> collection = database.getCollection(watchCollection);
        List<Bson> pipeline = Arrays
                .asList(Aggregates.match(Filters.in("operationType", Arrays.asList("insert", "update"))));
        ChangeStreamIterable<Document> changeStream = database.watch(pipeline).fullDocument(FullDocument.UPDATE_LOOKUP);
        changeStream.forEach(event -> {
            callback.accept(event);
        });
    }
    
    private void init(String dbName) {
        CodecProvider pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build();
        CodecRegistry pojoCodecRegistry = fromRegistries(getDefaultCodecRegistry(),CodecRegistries.fromCodecs(new PlayerCodec()),fromProviders(pojoCodecProvider));
        mongoClient = MongoClients.create(uri);
        mongoAccess = mongoClient.getDatabase(dbName).withCodecRegistry(pojoCodecRegistry);
        
        // option
        replaceOptions = new ReplaceOptions().upsert(true);
        updateOptions = new UpdateOptions().upsert(false);
    }
    
    public boolean save(BasePojo pojo) {
        MongoCollection<BasePojo> collection = mongoAccess
                .getCollection(pojo.getClass().getSimpleName().toLowerCase(Locale.ROOT), BasePojo.class);
        UpdateResult r = collection.replaceOne(eq(pojo.getId()), pojo, replaceOptions);
        return r.getUpsertedId() != null || r.getModifiedCount() > 0;
    }
    
    public boolean insert(BasePojo pojo) {
        String collectionName = pojo.getClass().getSimpleName().toLowerCase(Locale.ROOT);
        MongoCollection<BasePojo> collection = mongoAccess.getCollection(collectionName, BasePojo.class);
        InsertOneResult r = collection.insertOne(pojo);
        return r.getInsertedId() != null;
    }
    
    public <T> T find(Bson bson, Class<? extends BasePojo> clazz) {
        String collectionName = clazz.getSimpleName().toLowerCase(Locale.ROOT);
        return (T) mongoAccess.getCollection(collectionName, clazz).find(bson).first();
    }
    
    protected MongoCollection<JSONObject> getCollection(String collectionName) {
        return mongoAccess.getCollection(collectionName, JSONObject.class);
    }
    
}
